package com.bz.websocket.utils;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Created on 2019/1/19
 *
 * @author zhongyongbin
 */
@Component
public final class ObjectMapperUtil implements ApplicationContextAware {

    private static ObjectMapper objectMapper;

    private ObjectMapperUtil() {
    }

    /**
     * 将对象序列化为 json 字符串
     *
     * @param obj 待序列化对象
     * @return json 字符串
     */
    public static String transferToString(Object obj) {
        if (Objects.isNull(obj)) {
            return null;
        }
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (Exception e) {
            throw new JsonException("json serialization error.", e);
        }
    }

    /**
     * 将对象序列化为格式化的 json 字符串
     *
     * @param obj 待序列化对象
     * @return json 字符串
     */
    public static String transferToStringPretty(Object obj) {
        if (Objects.isNull(obj)) {
            return null;
        }
        try {
            return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
        } catch (Exception e) {
            throw new JsonException("json serialization error.", e);
        }
    }

    /**
     * 将 json 字符串反序列化为对象
     *
     * @param json  json 字符串
     * @param clazz 对象类型
     * @param <T>   要转换的对象类型
     * @return 通过反序列化得到的对象
     */
    public static <T> T transferToObject(String json, Class<T> clazz) {
        if (StringUtils.isBlank(json)) {
            return null;
        }
        try {
            return objectMapper.readValue(json, clazz);
        } catch (Exception e) {
            throw new JsonException("json deserialization error.", e);
        }
    }

    /**
     * 将 json 字符串反序列化为泛型对象
     *
     * @param json             json 字符串
     * @param clazz            对象类型
     * @param parameterClasses 泛型参数类型
     * @param <T>              要转换的对象类型
     * @return 通过反序列化得到的对象
     */
    public static <T> T transferToObject(String json, Class<?> clazz, Class<?>... parameterClasses) {
        if (StringUtils.isBlank(json)) {
            return null;
        }
        try {
            JavaType javaType = objectMapper.getTypeFactory().constructParametricType(clazz, parameterClasses);
            return objectMapper.readValue(json, javaType);
        } catch (Exception e) {
            throw new JsonException("json deserialization error.", e);
        }
    }

    /**
     * 将 json 字符串反序列化为泛型对象
     *
     * @param json          json 字符串
     * @param typeReference 类型参数
     * @param <T>           要转换的对象类型
     * @return 通过反序列化得到的对象
     */
    public static <T> T transferToObject(String json, TypeReference<T> typeReference) {
        if (StringUtils.isBlank(json)) {
            return null;
        }
        try {
            return objectMapper.readValue(json, typeReference);
        } catch (Exception e) {
            throw new JsonException("json deserialization error.", e);
        }
    }

    /**
     * 将 json 字符串反序列化为列表对象
     *
     * @param json          json 字符串
     * @param typeReference 参数类型
     * @param <T>           要转换的对象类型
     * @return 通过反序列化得到的列表
     */
    public static <T> List<T> transferToList(String json, TypeReference<List<T>> typeReference) {
        if (StringUtils.isNotBlank(json)) {
            try {
                return objectMapper.readValue(json, typeReference);
            } catch (Exception e) {
                throw new JsonException("json deserialization error.", e);
            }
        }

        return Collections.emptyList();
    }

    /**
     * 将 Map 对象转化为指定对象
     *
     * @param source 源数据
     * @param clazz  参数类型
     * @param <T>    要转换的对象类型
     * @return 转换得到的对象
     */
    public static <T> T transferMapToObject(Map<String, Object> source, Class<T> clazz) {
        if (MapUtils.isNotEmpty(source)) {
            try {
                return objectMapper.convertValue(source, clazz);
            } catch (Exception e) {
                throw new JsonException("json deserialization error.", e);
            }
        }

        return null;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        objectMapper = applicationContext.getBean(ObjectMapper.class);
    }

    public static class JsonException extends RuntimeException {

        JsonException(String message, Throwable e) {
            super(message, e);
        }
    }

}
